/* Copyright (c) 2009-2012 Matthias Kaeppler
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.mdroid.support.widgets;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.os.Message;
import android.util.AttributeSet;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ProgressBar;

import com.mdroid.R;
import com.mdroid.app.MLibrary;
import com.mdroid.support.images.remote.RemoteImageLoader;
import com.mdroid.support.images.remote.RemoteImageLoaderHandler;

/**
 * An image view that fetches its image off the web using the supplied URL. While the image is being
 * downloaded, a progress indicator will be shown. The following attributes are supported:
 * <ul>
 * <li>mdroid:imageUrl (String) -- The URL at which the image is found online</li>
 * <li>mdroid:autoLoad (Boolean) -- Whether the download should start immediately after view
 * inflation. Default value: true</li>
 * <li>mdroid:errorDrawable (Drawable) -- The drawable to display if the image download fails</li>
 * <li>mdroid:defaultDrawable (Drawable) -- The drawable to display when the image downloading</li>
 * </ul>
 * 
 * @author William Cheng
 */
public class RemoteImageView extends FrameLayout {

    private String imageUrl;

    private boolean autoLoad, isLoaded, isLoading;

    private ProgressBar loadingSpinner;
    private ImageView imageView;

    private Drawable errorDrawable, defaultDrawable;
    
    private RemoteImageLoader imageLoader;
    
    private FailedOnClickListener failListener;

    /**
     * @param context
     *            the view's current context
     * @param imageUrl
     *            the URL of the image to download and show
     * @param autoLoad
     *            Whether the download should start immediately after creating the view. If set to
     *            false, use {@link #loadImage()} to manually trigger the image download.
     */
    public RemoteImageView(Context context, String imageUrl, boolean autoLoad) {
        super(context);
        initialize(imageUrl, null, null, autoLoad);
    }

    /**
     * @param context
     *            the view's current context
     * @param imageUrl
     *            the URL of the image to download and show
     * @param defaultDrawable
     *            the imageView's default drawable
     * @param errorDrawable
     *            the drawable to be used if a download error occurs
     * @param autoLoad
     *            Whether the download should start immediately after creating the view. If set to
     *            false, use {@link #loadImage()} to manually trigger the image download.
     */
    public RemoteImageView(Context context, String imageUrl, Drawable defaultDrawable,
            Drawable errorDrawable, boolean autoLoad) {
        super(context);
        initialize(imageUrl, defaultDrawable, errorDrawable, autoLoad);
    }

    public RemoteImageView(Context context, AttributeSet attributes) {
        super(context, attributes);
        TypedArray a = context.obtainStyledAttributes(attributes, R.styleable.RemoteImageView);
        int defaultDrawableId = a.getResourceId(R.styleable.RemoteImageView_defaultDrawable, 0);
        Drawable defaultDrawable = null;
        if (defaultDrawableId > 0) {
            defaultDrawable = context.getResources().getDrawable(defaultDrawableId);
        }
        
        int errorDrawableId = a.getResourceId(R.styleable.RemoteImageView_errorDrawable, 0);
        Drawable errorDrawable = null;
        if (errorDrawableId > 0) {
            errorDrawable = context.getResources().getDrawable(errorDrawableId);
        }
        
        String imageUrl = a.getString(R.styleable.RemoteImageView_imageUrl);
        
        boolean autoLoad = a.getBoolean(R.styleable.RemoteImageView_autoLoad, true);
        a.recycle();
        
        initialize(imageUrl, defaultDrawable, errorDrawable, autoLoad);
    }
    
    @Override
    protected void onFinishInflate() {
        imageView = (ImageView) findViewById(R.id.image);
        if (imageView == null) {
            throw new RuntimeException(
                    "Your content must have a ImageView whose id attribute is " +
                    "'R.id.image'");
        }
        loadingSpinner = (ProgressBar) findViewById(R.id.progress);
        if (autoLoad && imageUrl != null) {
            loadImage();
        } else {
            imageView.setImageDrawable(defaultDrawable);
            unloading();
        }
    }

    private void initialize(String imageUrl, Drawable defaultDrawable, Drawable errorDrawable, boolean autoLoad) {
        this.imageUrl = imageUrl;
        this.autoLoad = autoLoad;
        this.defaultDrawable = defaultDrawable;
        this.errorDrawable = errorDrawable;
        this.imageLoader = MLibrary.Instance().getImageLoader();
        setClickable(true);
    }

    /**
     * Use this method to trigger the image download if you had previously set autoLoad to false.
     */
    public void loadImage() {
        loading();
        imageView.setImageDrawable(defaultDrawable);
        isLoading = true;
        isLoaded = false;
        imageLoader.loadImage(imageUrl, imageView, loadingSpinner, new DefaultImageLoaderHandler());
    }

    public boolean isLoaded() {
        return isLoaded;
    }

    public void setImageUrl(String imageUrl) {
        this.imageUrl = imageUrl;
    }

    /**
     * Often you have resources which usually have an image, but some don't. For these cases, use
     * this method to supply a placeholder drawable which will be loaded instead of a web image.
     * 
     * @param imageResourceId
     *            the resource of the placeholder image drawable
     */
    public void setNoImageDrawable(int imageResourceId) {
        imageView.setImageResource(imageResourceId);
        unloading();
    }

    public void reset() {
        isLoading = false;
        isLoaded = false;
        unloading();
        setTag(null);
        imageView.setImageDrawable(defaultDrawable);
    }

    private class DefaultImageLoaderHandler extends RemoteImageLoaderHandler {

        public DefaultImageLoaderHandler() {
            super(imageView, imageUrl, defaultDrawable, errorDrawable, loadingSpinner);
        }

        @Override
        protected boolean handleImageLoaded(Bitmap bitmap, Message msg) {
            isLoaded = super.handleImageLoaded(bitmap, msg);
            isLoading = false;
            unloading();
            return isLoaded;
        }
    }

    /**
     * Returns the URL of the image to show. Corresponds to the view attribute ignition:imageUrl.
     * 
     * @return the image URL
     */
    public String getImageUrl() {
        return imageUrl;
    }

    /**
     * Whether or not the image should be downloaded immediately after view inflation. Corresponds
     * to the view attribute ignition:autoLoad (default: true).
     * 
     * @return true if auto downloading of the image is enabled
     */
    public boolean isAutoLoad() {
        return autoLoad;
    }

    /**
     * The drawable that will be shown when the image download fails. Corresponds to the view
     * attribute ignition:errorDrawable. If left blank, a stock alert icon from the Android platform
     * will be used.
     * 
     * @return the error drawable
     */
    public Drawable getErrorDrawable() {
        return errorDrawable;
    }

    /**
     * The image view that will render the downloaded image.
     * 
     * @return the {@link ImageView}
     */
    public ImageView getImageView() {
        return imageView;
    }

    /**
     * The progress bar that is shown while the image is loaded.
     * 
     * @return the {@link ProgressBar}
     */
    public ProgressBar getProgressBar() {
        return loadingSpinner;
    }
    
    private void loading() {
        if (loadingSpinner != null) {
            loadingSpinner.setVisibility(VISIBLE);
        }
        imageView.setVisibility(VISIBLE);
    }
    
    private void unloading() {
        if (loadingSpinner != null) {
            Animation anim = new AlphaAnimation(1f, 0f);
            anim.setDuration(200);
            loadingSpinner.startAnimation(anim);
            loadingSpinner.setVisibility(GONE);
            loadingSpinner.setProgress(0);
        }
        if (isLoaded) {
            Animation anim = new AlphaAnimation(.3f, 1f);
            anim.setDuration(200);
            imageView.startAnimation(anim);
        }
        imageView.setVisibility(VISIBLE);
    }
    
    @Override
    public boolean performClick() {
        if (!isLoading && !isLoaded && failListener != null) {
            failListener.onClick(this);
        }
        return super.performClick();
    }
    
    public void setFailedListener(FailedOnClickListener failListener) {
        this.failListener = failListener;
    }

    public interface FailedOnClickListener {
        void onClick(RemoteImageView v);
    }
}
